#ifndef LOCAL_MINIMIZER_IMPLEMENTATION_H_
#define LOCAL_MINIMIZER_IMPLEMENTATION_H_

#include <cmath>

#include "local_minimizer.h"

namespace local_minimizer_implementation
{

class AxisChangingDirector : public local_minimizer::Director
{
public:
	Vector operator() (const local_minimizer::Function& function, const Vector& position, int iteration) const
	{
		Vector direction(position.length());
		direction[iteration%direction.length()]=1.0;
		return direction;
	}
};

class GoldenSearcher : public local_minimizer::Searcher
{
private:
	double bound_;
	double tolerance_;
	double phi_;

public:
	GoldenSearcher(const double bound, const double tolerance) : bound_(fabs(bound)), tolerance_(fabs(tolerance)), phi_((1+sqrt(5.0))/2.0) {}

	double operator() (const local_minimizer::Function& function, const Vector& position, const Vector& direction) const
	{
		double a=-bound_;
		double b=bound_;
		double c1=b-(b-a)/phi_;
		double c2=a+(b-a)/phi_;
		double y1=function(position+(direction*c1));
		double y2=function(position+(direction*c2));

		do
		{
			if(y1>y2) { a=c1; }
			else { b=c2; }
			c1=b-(b-a)/phi_;
			c2=a+(b-a)/phi_;
			y1=function(position+(direction*c1));
			y2=function(position+(direction*c2));
		}
		while((b-a)>tolerance_);

		return (y1<=y2 ? c1 : c2);
	}
};

class ConvergenceChecker : public local_minimizer::Checker
{
private:
	double epsilon_;
	int acceptance_level_;
	std::pair<double, double> interval_;
	int level_;

public:
	ConvergenceChecker(const double epsilon, const int acceptance_level) : epsilon_(epsilon), acceptance_level_(acceptance_level), interval_(0.0, 0.0), level_(0) {}

	bool operator() (const local_minimizer::Function& function, const Vector& position, int iteration)
	{
		const double value=function(position);

		if(value>=interval_.first && value<=interval_.second)
		{
			level_++;
		}
		else
		{
			level_=0;
			interval_=std::make_pair(value-epsilon_, value+epsilon_);
		}

		std::cout << iteration << "\t" << position.to_string() << "\t" << value << "\n";

		return (level_>=acceptance_level_);
	}
};

}

#endif /* LOCAL_MINIMIZER_IMPLEMENTATION_H_ */
